﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NCalculator.Operations;

namespace NCalculator
{
    public class Solve 
    {
        #region Attributes
        private List<Node> _opList;
        private HistoryContainer _historyContainer;
        #endregion

        public Solve()
        {
            _opList = new List<Node>();
        }

        public Solve(HistoryContainer historyContainer)
        {
            _historyContainer = historyContainer;
            _opList = new List<Node>();
        }

        /// <summary>
        /// Insert new value to calculate
        /// </summary>
        /// <param name="node"></param>
        public void InsertValue(Node node)
        {
            _opList.Add(node);
        }

        /// <summary>
        /// Calculate value of equation
        /// </summary>
        /// <returns>result of equation</returns>
        public double Evaluation()
        {
            // saving it to history
            SingleHistoryLine historyLine = new SingleHistoryLine();
            historyLine.CopyHistoryLine(_opList);
            _historyContainer.AddHistoryLine(historyLine);

            OperationNode tmp; // node with the highest priority
            List<Node> tmpList = _opList;
            int pos = 0;

            while (tmpList.Count > 1)
            {
                tmp = null;
                pos = 0;
                foreach (Node n in tmpList)
                {
                    // searching for heighest priority
                    if (n is OperationNode)
                    {
                        if (tmp == null)
                        {
                            tmp = (OperationNode)n;
                            pos = tmpList.IndexOf(n);
                        }
                        else if (tmp.Priority > ((OperationNode)n).Priority)
                        {
                            tmp = (OperationNode)n;
                            pos = tmpList.IndexOf(n);
                        }
                    }
                }

                if (tmpList.Count > 3)
                {
                    tmp.Left = tmpList[pos - 1];
                    tmp.Right = tmpList[pos + 1];
                    ValueNode newNode = new ValueNode(tmp.Evaluation());

                    // removing left, right and operation node
                    tmpList.Remove(tmpList[pos - 1]);
                    tmpList.Remove(tmpList[pos]);

                    //replaced operation node with value node
                    tmpList.Insert(tmpList.IndexOf(tmp), newNode);
                    tmpList.Remove(tmp);
                }
                else
                {
                    ((OperationNode)tmpList[1]).Right = tmpList[2];
                    ((OperationNode)tmpList[1]).Left = tmpList[0];
                    tmpList.RemoveAt(0);
                    tmpList.RemoveAt(1);
                }
            }
            double result = 0;
            try
            {
                result = tmpList[0].Evaluation();
            }
            catch (Exception ex)
            {
                _opList.Clear();
                tmpList.Clear();
                result = 0;
                throw ex; // throwing exception further
            }
            finally
            {
                _opList.Clear();
            }

            return result; 
        }

        /// <summary>
        /// Shows equation as string
        /// </summary>
        /// <returns>string</returns>
        public override string ToString()
        {
            StringBuilder output = new StringBuilder();
            foreach (Node n in _opList)
            {
                output.Append(n.ToString());
            }

            return output.ToString();
        }
    }
}
